import { VisualizationEvent } from "../components/FeatureProcessor";

export class BackgroundLayer {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private stars: Star[] = [];
  private blackHole: BlackHole;
  private isPlaying: boolean = false;
  private animationFrameId: number | null = null;

  constructor(canvas: HTMLCanvasElement) {
    this.canvas = canvas;
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      throw new Error('Unable to get 2D context');
    }
    this.ctx = ctx;
    
    // Initialize black hole at the center
    this.blackHole = new BlackHole(
      this.canvas.width / 2,
      this.canvas.height / 2,
      50 // Initial radius
    );
    
    // Create initial stars
    this.createStars(200);
    
    // Handle window resize
    window.addEventListener('resize', () => {
      this.handleResize();
    });
  }

  private handleResize(): void {
    // Update black hole position to center
    this.blackHole.x = this.canvas.width / 2;
    this.blackHole.y = this.canvas.height / 2;
  }

  private createStars(count: number): void {
    for (let i = 0; i < count; i++) {
      // Create stars at random positions
      const star = new Star(
        Math.random() * this.canvas.width,
        Math.random() * this.canvas.height,
        Math.random() * 2 + 1, // radius between 1 and 3
        Math.random() * 0.5 + 0.1 // opacity between 0.1 and 0.6
      );
      this.stars.push(star);
    }
  }

  public update(event: VisualizationEvent): void {
    if (!this.isPlaying) return;
    
    // Update black hole size based on audio energy (RMS)
    // Limit the maximum size to prevent it from dominating the screen
    this.blackHole.radius = Math.min(150, 50 + event.rms * 100000);
    
    // Update black hole intensity based on spectral centroid
    // Limit the intensity to prevent over-saturation
    this.blackHole.intensity = Math.min(0.8, event.spectralCentroid / 10000);
    
    // Continuously add new stars during playback
    // Increased probability to ensure stars keep appearing
    if (Math.random() < 0.3) {
      this.addStar();
    }
    
    // Update stars
    for (let i = this.stars.length - 1; i >= 0; i--) {
      const star = this.stars[i];
      
      // Calculate distance to black hole
      const dx = star.x - this.blackHole.x;
      const dy = star.y - this.blackHole.y;
      const distance = Math.sqrt(dx * dx + dy * dy);
      
      // If star is close enough to black hole, remove it
      if (distance < this.blackHole.radius) {
        this.stars.splice(i, 1);
        continue;
      }
      
      // Apply gravitational pull towards black hole
      // Reduced force to slow down stars
      const force = 0.05 + (this.blackHole.radius / distance) * 0.1;
      star.vx -= (dx / distance) * force * (0.5 + event.zeroCrossingRate * 2);
      star.vy -= (dy / distance) * force * (0.5 + event.zeroCrossingRate * 2);
      
      // Update star position
      star.x += star.vx;
      star.y += star.vy;
      
      // Wrap around edges
      if (star.x < 0) star.x = this.canvas.width;
      if (star.x > this.canvas.width) star.x = 0;
      if (star.y < 0) star.y = this.canvas.height;
      if (star.y > this.canvas.height) star.y = 0;
    }
    
    this.draw();
  }

  private addStar(): void {
    // Add a star at a random edge of the canvas
    const side = Math.floor(Math.random() * 4);
    let x, y;
    
    switch (side) {
      case 0: // top
        x = Math.random() * this.canvas.width;
        y = 0;
        break;
      case 1: // right
        x = this.canvas.width;
        y = Math.random() * this.canvas.height;
        break;
      case 2: // bottom
        x = Math.random() * this.canvas.width;
        y = this.canvas.height;
        break;
      case 3: // left
        x = 0;
        y = Math.random() * this.canvas.height;
        break;
    }
    
    const star = new Star(
      x,
      y,
      Math.random() * 2 + 1,
      Math.random() * 0.5 + 0.1
    );
    
    // Give it a slower initial velocity towards the center
    const dx = this.blackHole.x - x;
    const dy = this.blackHole.y - y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    star.vx = (dx / distance) * (Math.random() * 0.1 + 0.05);
    star.vy = (dy / distance) * (Math.random() * 0.1 + 0.05);
    
    this.stars.push(star);
  }

  private draw(): void {
    // Clear canvas with a semi-transparent black for trail effect
    // Reduced alpha for less persistence
    this.ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    
    // Draw stars
    for (const star of this.stars) {
      this.ctx.beginPath();
      this.ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);
      this.ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`;
      this.ctx.fill();
    }
    
    // Draw black hole
    const gradient = this.ctx.createRadialGradient(
      this.blackHole.x, this.blackHole.y, 0,
      this.blackHole.x, this.blackHole.y, this.blackHole.radius
    );
    
    // Adjusted colors to reduce red dominance
    gradient.addColorStop(0, `rgba(255, 100, 0, ${this.blackHole.intensity})`);
    gradient.addColorStop(0.5, `rgba(150, 0, 150, ${this.blackHole.intensity * 0.7})`);
    gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
    
    this.ctx.beginPath();
    this.ctx.arc(this.blackHole.x, this.blackHole.y, this.blackHole.radius, 0, Math.PI * 2);
    this.ctx.fillStyle = gradient;
    this.ctx.fill();
  }

  public play(): void {
    this.isPlaying = true;
    this.animate();
  }

  public pause(): void {
    this.isPlaying = false;
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
      this.animationFrameId = null;
    }
  }

  private animate(): void {
    if (!this.isPlaying) return;
    
    this.draw();
    this.animationFrameId = requestAnimationFrame(() => this.animate());
  }

  public resize(width: number, height: number): void {
    this.canvas.width = width;
    this.canvas.height = height;
    this.handleResize();
  }
}

class Star {
  x: number;
  y: number;
  radius: number;
  opacity: number;
  vx: number;
  vy: number;
  
  constructor(x: number, y: number, radius: number, opacity: number) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.opacity = opacity;
    // Reduced initial velocity for slower movement
    this.vx = (Math.random() - 0.5) * 0.1;
    this.vy = (Math.random() - 0.5) * 0.1;
  }
}

class BlackHole {
  x: number;
  y: number;
  radius: number;
  intensity: number;
  
  constructor(x: number, y: number, radius: number) {
    this.x = x;
    this.y = y;
    this.radius = radius;
    this.intensity = 0.8;
  }
}